package com.iot.core.manager.htctl;


import com.iot.core.annotation.Auth;
import com.iot.core.annotation.Log;
import com.iot.core.data.entity.HtAction;
import com.iot.core.data.entity.HtCls;
import com.iot.core.data.entity.HtCodeLog;
import com.iot.core.data.entity.HtCtl;
import com.iot.core.data.mapper.HtActionMapper;
import com.iot.core.data.mapper.HtClsMapper;
import com.iot.core.data.mapper.HtCodeLogMapper;
import com.iot.core.data.mapper.HtCtlMapper;
import com.iot.core.enums.AuthType;
import com.iot.core.manager.htcls.HtClsDto;
import com.iot.core.manager.htcls.HtClsManager;
import com.iot.core.manager.id.HtIdManager;
import com.iot.core.util.StreamUtil;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.iot.core.data.entity.table.HtActionTableDef.HT_ACTION;
import static com.iot.core.data.entity.table.HtClsTableDef.HT_CLS;
import static com.iot.core.data.entity.table.HtCtlTableDef.HT_CTL;

@Slf4j
public class HtCtlManager {
    @Autowired
    HtCodeLogMapper htCodeLogMapper;
    @Autowired
    HtClsMapper htClsMapper;
    @Autowired
    HtCtlMapper htCtlMapper;
    @Autowired
    HtActionMapper htActionMapper;


    private static   HtCtlManager instance = null;


    //2.构造方法设为 private
    public HtCtlManager() {
       instance=this;
    }

    public static HtCtlManager getInstance() {
        return instance;
    }

    //region 扫描本地packge
    List<HtCtlDto> lCtlAll;
    List<HtCtlActDto> lActAll;

    public void start() {
        lCtlAll=new ArrayList<>();
        lActAll=new ArrayList<>();
        Reflections reflections = new Reflections("com.iot");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Tag.class);
        for (Class<?> clazz : annotated) {
            var mcls = getHtClsDto(clazz);
            if(mcls!=null){
                lCtlAll.add(mcls);
            }
        }
        var l1= lCtlAll.stream().map(a->a.getActions()  )
                .flatMap(a->a.stream())
                .collect(Collectors.toList());
        lActAll=l1;

    }

    HtCtlDto getHtClsDto(Class<?> t){
        var RequestMapping= t.getAnnotation(RequestMapping.class);
        if(RequestMapping==null){
            return null;
        }

        var mcls=new HtCtlDto();
        mcls.setEname(t.getSimpleName() );
        mcls.setName(t.getSimpleName());
        mcls.setClazz(t);
        mcls.setMemo("");
        mcls.setRoute(RequestMapping.value()[0])  ;
        mcls.setFullName(t.getName());
        var apiModel= t.getAnnotation(Tag.class);
        if(apiModel!=null){
            mcls.setName(apiModel.name() );
            mcls.setMemo(apiModel.description() );
        }
        getHTCtlActDto(mcls);
        return mcls;
    }
    void getHTCtlActDto (HtCtlDto mcls){
        Class<?> t = mcls.getClazz();

        var r = new ArrayList<HtCtlActDto>();
        mcls.setActions(r);

        var methods = t.getDeclaredMethods( );
        for(var p:methods){
            var apiModel= p.getAnnotation(Operation.class);
            if(apiModel==null){
                continue;
            }


            var url="";
            if("".equals(url)){
                var mapping= p.getAnnotation(PostMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }
            if("".equals(url)){
                var mapping= p.getAnnotation(GetMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }
            if("".equals(url)){
                var mapping= p.getAnnotation(RequestMapping.class);
                if(mapping!=null){
                    url=mapping.value()[0];
                }
            }

            if("".equals(url)) {
                    continue;
            }

            var f=new HtCtlActDto();
            f.setEname(p.getName());
            f.setName(apiModel.summary() );
            f.setMemo(apiModel.description());
            f.setAuthType(AuthType.None);
            f.setFullUrl(mcls.getRoute()+url);
            f.setLogTemplete("");
            f.setMinfo(p);
            f.setCtlFullName(mcls.getFullName());

            var auth= p.getAnnotation(Auth.class);
            if(auth!=null){
                f.setAuthType(auth.authType());
            }

            var log=p.getAnnotation(Log.class);
            if(log!=null){
                f.setLogNeed(log.logNeed());
                if(StringUtils.hasText(log.logTemplete())){
                    f.setLogTemplete(log.logTemplete());
                }
                f.setLogPin(log.logPin());
                f.setLogPout(log.logPout());
            }

            if("".equals(f.getLogTemplete())){
                if(f.getEname().startsWith("delete")){
                    f.setLogTemplete("{ActionName} {Deleted.Name}");
                }else{
                    f.setLogTemplete("{ActionName} {Pin.Name}");
                }
            }

            var ps = p.getParameters();
            for ( var mps : ps )
            {
                var req=mps.getAnnotation(RequestParam.class);
                if(req!=null){
                    setPinType(f,mps);
                }

                var requestBody=mps.getAnnotation(RequestBody.class);
                if(requestBody!=null){
                    setPinType(f,mps);
                }
            }
            f.setPoutType(p.getReturnType().getName() );
            if(List.class.isAssignableFrom(p.getReturnType() ) )
            {
                f.setPoutIslist(true);
                ParameterizedType py=(ParameterizedType)(p.getGenericReturnType());
                f.setPoutType(  py.getActualTypeArguments()[0].getTypeName() );
            }
            if(Page.class.isAssignableFrom(p.getReturnType()  ) ) {
                f.setPoutIspage(true);
                ParameterizedType py=(ParameterizedType)(p.getGenericReturnType());
                f.setPoutType(  py.getActualTypeArguments()[0].getTypeName() );
            }
            r.add(f);
        }

    }
    void setPinType(HtCtlActDto f, Parameter mps){
        f.setPinName(mps.getName());
        f.setPinType(mps.getType().getTypeName() );
        if(List.class.isAssignableFrom(mps.getType() ) )
        {
            f.setPinIslist(true);
            ParameterizedType py=(ParameterizedType)(mps.getParameterizedType());
            f.setPinType(  py.getActualTypeArguments()[0].getTypeName() );

        }

    }

    //endregion 扫描本地packge


    //region  同步数据库
    HtCodeLog createHTCodeLogByDto(HtCtl mdb){
        var mcodelog=new HtCodeLog();
        mcodelog.setId(HtIdManager.getInstance().nextLong());
        mcodelog.setBid(mdb.getId());
        mcodelog.setCrtDt(new Date());
        mcodelog.setFullName(mdb.getCtlFullName());
        mcodelog.setLx("Ctl");
        mcodelog.setMemo("");
        mcodelog.setName(mdb.getName());
        mcodelog.setPrjName(mdb.getPrjName());
        return mcodelog;
    }
    HtCodeLog createHTCodeLogByDto(HtAction mdb){
        var mcodelog=new HtCodeLog();
        mcodelog.setId(HtIdManager.getInstance().nextLong());
        mcodelog.setBid(mdb.getId());
        mcodelog.setCrtDt(new Date());
        mcodelog.setFullName(mdb.getFullUrl() );
        mcodelog.setLx("Action");
        mcodelog.setMemo("");
        mcodelog.setName(mdb.getName());
        mcodelog.setPrjName(mdb.getPrjName());
        return mcodelog;
    }

    List<HtCtl> ldbctl=null;
    List<HtAction> ldbact=null;

    List<HtCls> ldbcls=null;
    String prjName="";

    List<HtCtlDto> lprjctl=null;
    List<HtCtlActDto> lprjact=null;

    @Transactional(rollbackFor = Exception.class)
    public void refreshToDb(String prjName){
        this.prjName=prjName;
        lprjctl=lCtlAll.stream().filter(a->a.getFullName().startsWith(prjName)).collect(Collectors.toList());
        lprjact=lActAll.stream().filter(a->a.getCtlFullName().startsWith(prjName)).collect(Collectors.toList());

        LogicDeleteManager.execWithoutLogicDelete(()->{
            ldbcls=htClsMapper.selectAll();
        });

        LogicDeleteManager.execWithoutLogicDelete(()->{
            var qw= QueryWrapper.create();
            qw.where(HT_CTL.PRJ_NAME.eq(prjName));
            ldbctl=htCtlMapper.selectListByQuery(qw);
        });

        LogicDeleteManager.execWithoutLogicDelete(()->{
            var qw= QueryWrapper.create();
            qw.where(HT_ACTION.PRJ_NAME.eq(prjName));
            ldbact=htActionMapper.selectListByQuery(qw);
        });
        refreshCtlToDB();
        refreshActionToDB();
        ldbcls=null;
        ldbctl=null;
        ldbact=null;

        lprjctl=null;
        lprjact=null;
    }




    void refreshCtlToDB(){

        for(var mcls : lprjctl)
        {
            dealCtrl(mcls );
        }
        for (var mdb : ldbctl)
        {
            var op=lprjctl.stream().filter( a-> a.getFullName().equals(mdb.getCtlFullName()))
                    .findFirst()  ;

            if (!op.isPresent() && !mdb.isDeleted() )
            {
                htCtlMapper.deleteById(mdb,true);
            }
        }
    }
    void dealCtrl(HtCtlDto mcls ){
        var op=ldbctl.stream().filter(a->a.getCtlFullName().equals(mcls.getFullName()))
                .findFirst()
                 ;

        if(!op.isPresent()){
            var mdb=new HtCtl();
            mdb.setId(HtIdManager.getInstance().nextLong());
            mdb.setName(mcls.getName());
            mdb.setMemo(mcls.getMemo());
            mdb.setRoute(mcls.getRoute());
            mdb.setCtlFullName(mcls.getFullName());
            mdb.setPrjName( prjName );
            htCtlMapper.insert(mdb );

        }else{
            var mdb=op.get();
            if(!mdb.getName().equals(mcls.getName()) )
            {
                mdb.setName(mcls.getName() );
            }
            if(!mdb.getMemo().equals(mcls.getMemo()) )
            {
                mdb.setMemo(mcls.getMemo() );
            }
            if(!mdb.getRoute().equals(mcls.getRoute()) )
            {
                mdb.setRoute(mcls.getRoute() );
            }
            if(mdb.isDeleted() )
            {
                mdb.setDeleted(false);
            }
            htCtlMapper.updateById(mdb,true);
        }

    }


    void refreshActionToDB(){

        for(var mcls  :lprjact)
        {
            dealAction(mcls );
        }
        for(var mdb:ldbact){
            if(mdb.isDeleted()){
                continue;
            }
            var op=lprjact.stream().filter(a->a.getFullUrl().equals(mdb.getFullUrl()))
                    .findFirst();
            if(!op.isPresent()){

                htActionMapper.deleteById(mdb,true);

                var mcodelog= createHTCodeLogByDto(mdb);
                mcodelog.setMemo("删除");
                htCodeLogMapper.insert(mcodelog);
            }
        }
    }
    void dealAction(HtCtlActDto mcls ){
         var op=ldbact.stream().filter(a->a.getFullUrl().equals(mcls.getFullUrl()))
                 .findFirst() ;
         if(!op.isPresent() ){
                var mdb=new HtAction();
                mdb.setId(HtIdManager.getInstance().nextLong());
                mdb.setPrjName(prjName);
                mdb.setCtlFullName(mcls.getCtlFullName());
                mdb.setFullUrl(mcls.getFullUrl());
                mdb.setEname(mcls.getEname() );
                mdb.setName(mcls.getName());
                mdb.setMemo(mcls.getMemo());
                mdb.setPinType(mcls.getPinType());
                mdb.setPinIslist(mcls.isPinIslist());
                mdb.setPoutType(mcls.getPoutType());
                mdb.setPoutIslist(mcls.isPoutIslist());
                mdb.setPoutIspage(mcls.isPoutIspage());
                mdb.setAuthType(mcls.getAuthType().toString() );
                mdb.setLogNeed(mcls.isLogNeed());
                mdb.setLogTemplete(mcls.getLogTemplete());
                mdb.setLogPin(mcls.isLogPin());
                mdb.setLogPout(mcls.isLogPout());
                htActionMapper.insert(mdb  );

                 var mcodelog= createHTCodeLogByDto(mdb);
                 mcodelog.setMemo("新增");
                 htCodeLogMapper.insert(mcodelog );
         }
         else{
             var mdb=op.get();
             var msg=compareActionField(mcls,mdb);
             if (mdb.isDeleted())
             {
                 mdb.setDeleted(false);
                 msg = "删除恢复\r\n" + msg;
             }

             if (!"".equals(mdb.getPinType()))
             {
                 var op2=ldbcls.stream().filter(a -> a.getFullName().equals(mdb.getPinType()))
                         .findFirst();
                 if(op2.isPresent()){
                     var mcls2=op2.get();
                     if (  mcls2.getEdtDt().getTime()> mdb.getEdtDt().getTime() )
                     {

                         msg +=String.format("参数类型 %s 更新",mcls2.getFullName());
                     }
                 }

             }
             if (!"".equals(mdb.getPoutType()))
             {
                 var op2=ldbcls.stream().filter(a -> a.getFullName().equals(mdb.getPoutType()))
                         .findFirst();
                 if(op2.isPresent()){
                     var mcls2=op2.get();
                     if (   mcls2.getEdtDt().getTime()> mdb.getEdtDt().getTime() )
                     {
                         msg +=String.format("返回结果类型 %s 更新",mcls2.getFullName());
                     }
                 }

             }
             if (!"".equals(msg)){
                 htActionMapper.updateById(mdb,true);
                 var mcodelog= createHTCodeLogByDto(mdb);
                 mcodelog.setMemo(msg);
                 htCodeLogMapper.insert(mcodelog,true);

             }

         }
    }
    String compareAction(HtCtlActDto mcls, HtAction mdb,String fieldName){
        var v= HtClsManager.getInstance().getFieldStrV(mcls,fieldName);
        var dbv=HtClsManager.getInstance().getFieldStrV(mdb,fieldName);
        if(!v.equals(dbv)){
            var v2=HtClsManager.getInstance().getFieldV(mcls,fieldName);
            if(v2 instanceof Enum<?>){
                v2=v2.toString();
            }
            HtClsManager.getInstance().setFieldV(mdb,fieldName,v2);
            return fieldName+" "+dbv+"=>"+v+"\r\n";
        }
        return "";
    }

    String compareActionField(HtCtlActDto mcls, HtAction mdb )
    {
        var sb = new StringBuilder();
        sb.append(  compareAction(mcls,mdb,"ctlFullName") );
        sb.append(  compareAction(mcls,mdb,"fullUrl") );
        sb.append(  compareAction(mcls,mdb,"name") );
        sb.append(  compareAction(mcls,mdb,"memo") );
        sb.append(  compareAction(mcls,mdb,"pinType") );
        sb.append(  compareAction(mcls,mdb,"pinIslist") );
        sb.append(  compareAction(mcls,mdb,"poutType") );
        sb.append(  compareAction(mcls,mdb,"poutIslist") );
        sb.append(  compareAction(mcls,mdb,"poutIspage") );
        sb.append(  compareAction(mcls,mdb,"authType") );
        sb.append(  compareAction(mcls,mdb,"logNeed") );
        sb.append(  compareAction(mcls,mdb,"logTemplete") );
        sb.append(  compareAction(mcls,mdb,"logPin") );
        sb.append(  compareAction(mcls,mdb,"logPout") );
        return  sb.toString();
    }

    //endregion  同步数据库


    //region 公共方法
    public HtCtlDto getCtl(String fullname)
    {
        String finalUrl = fullname;
        var m = StreamUtil.get(lCtlAll, a->a.getFullName().equals(finalUrl));
        return m;
    }

    public HtCtlActDto getAction(String url)
    {
        url = url.toLowerCase();
        String finalUrl = url;
        var m = StreamUtil.get(lActAll,a->a.getFullUrl().equals(finalUrl));
        return m;
    }

    public HtCtlActDto getAction(String ctlFullName,String ename)
    {
        var m = StreamUtil.get(lActAll,a->a.getCtlFullName().equals(ctlFullName)
            &&a.getEname().equals(ename)
        );
        return m;
    }

    //endregion 公共方法
}
